﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using System.Threading;


namespace _3D_FileExplorer
{
    class DrawLeafs : DrawableGameComponent
    {
        List<GraphicalNode> leafs = new List<GraphicalNode>();
        public List<Primitives> gLeafs = new List<Primitives>();
        public List<Primitives> tempGleafs = new List<Primitives>();
        public static int progressBarValue;
        KeyboardControl kControl;

        int id,max,min,maxCreated = 0;
        private float startAngle = 0;
        public int start = 0;
        public int end = 96;
        string type, axis;
        Vector3 center;
        Matrix translation = Matrix.Identity;
        public static bool threadIsRunning = false;
        private bool tempGleafsReady = false;
        private int scrollWheelValue;
        private Matrix effectWorld = Matrix.Identity;
        public Primitives pickedLeaf;
        public Primitives lastPickedLeaf;
        int pickupdated = 0;
        double previousGameTime;
        double moveGameTime = 0;
        bool runOnce = false;
        Picking pick;
        int keyboardRotation = 0;
        int jumps = 0;
        int previousJump = 0;
        string rootCylinderPatch;
        private Cylinder background;

        public DrawLeafs(Primitives pickedCube,string rootCylinderPatch,Game game)
            : base(game)
        {
            Interface.messageBoard = "";
            this.rootCylinderPatch = rootCylinderPatch;
            this.id = Int16.Parse(pickedCube.name);
            this.type = pickedCube.type;
            this.center = pickedCube.center;
            this.translation = pickedCube.translation;
            scrollWheelValue = Mouse.GetState().ScrollWheelValue;
            tempGleafs.Clear();
            leafs = CreateGraphicsTree.nodeTypes[id][type].ToList();
            axis = FindAxis(pickedCube);
            createBackground(pickedCube);
            GetLimits();
            pick = new Picking();
            Thread t = new Thread(delegate() { SetTransform(0, 96, 0); });
            threadIsRunning = true;
            t.Start();
            SetCamera(Fe.camera, axis);
        }

        public override void Initialize()
        {
            kControl = new KeyboardControl();
            leafs = CreateGraphicsTree.nodeTypes[id][type].ToList();
            base.Initialize();
        }

        private void createBackground(Primitives rootCylinder)
        {
            Matrix templateTranslation = translation;
            switch (axis)
            {
                case "X":
                    templateTranslation *= Matrix.CreateTranslation(new Vector3(9, 0, 0));
                    break;
                case "Y":
                    templateTranslation *= Matrix.CreateTranslation(new Vector3(0, 9, 0));
                    break;
                case "Z":
                    templateTranslation *= Matrix.CreateTranslation(new Vector3(0, 0, 9));
                    break;
            }
            background = new Cylinder(templateTranslation,rootCylinder.rotation, new Color(Color.White,50), "", "", "", 777, 1, 0, 18, 9.8f, 50, Vector3.Zero, 0);
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        
        public void Create(int createTo)
        {
            int i,j =0;
            for ( i = maxCreated; i < createTo; i++)
            {
                j++;
                tempGleafs.Add(new LeafPrimitive(center, leafs[i].Name, leafs[i].ShortName, type,leafs[i].id,0, translation));
                Fe.form.StatusLabel.Text = "Loading file: " + leafs[i].ShortName;
                progressBarValue = 100 / (createTo - maxCreated)*j;
            }
            if (maxCreated < createTo)
            {
                maxCreated = createTo;
            }
            progressBarValue = 0;
        }

        public void SetTransform(int start, int eEnd, float startAngle)
        {          
            float drift = -0.75f;
            int i = 0;
            float fullangle = startAngle;
            this.end = eEnd;
            float da = (float)Math.Cos(startAngle);
            float db = (float)Math.Sin(startAngle);
            if (end > max) end = max;
            Create(end);     

            for (i = start; i < end;i++)
            {

                float radius = 10f;
                if ((i % 6) == 0)
                {
                    fullangle -= MathHelper.PiOver4 / 2;
                    da = (float)Math.Cos(fullangle);
                    db = (float)Math.Sin(fullangle);
                    drift = -0.75f;
                }

                switch (axis)
                {
                    case "X":
                        ((LeafPrimitive)tempGleafs[i]).SetTransform(Matrix.CreateRotationX(-fullangle + MathHelper.PiOver2), Matrix.CreateTranslation(new Vector3(drift, db, da) * radius) * Matrix.CreateRotationX(-MathHelper.PiOver4));
                        break;
                    case "Y":
                        ((LeafPrimitive)tempGleafs[i]).SetTransform(Matrix.CreateRotationZ(-MathHelper.PiOver2) * Matrix.CreateRotationX(-MathHelper.Pi) * Matrix.CreateRotationY(-fullangle), Matrix.CreateTranslation(new Vector3(da, drift, db) * radius) * Matrix.CreateRotationY(-3 * MathHelper.PiOver4));
                        break;

                    case "Z":
                        ((LeafPrimitive)tempGleafs[i]).SetTransform(Matrix.CreateRotationY(-MathHelper.PiOver2) * Matrix.CreateRotationZ(-fullangle - MathHelper.PiOver2), Matrix.CreateTranslation(new Vector3(da, -db, drift) * radius) * Matrix.CreateRotationZ(MathHelper.PiOver2 + MathHelper.PiOver4));
                        break;

                }
                drift += 0.3f;

            }
            Fe.form.StatusLabel.Text = "";
            threadIsRunning = false;
            tempGleafsReady = true;
        }

        public void SetCamera(Camera camera, string axis)
        {

            switch (axis)
            {
                case "X":
                    camera.cameraPosition = new Vector3(center.X, center.Y + 5, center.Z + 30);
                    camera.cameraDirection = center - camera.cameraPosition;
                    camera.cameraDirection.Normalize();
                    camera.cameraUp = new Vector3(0, 1, 0);
                    break;
                case "Y":
                    camera.cameraPosition = new Vector3(center.X-5, center.Y, center.Z + 30);
                    camera.cameraDirection = center - camera.cameraPosition;
                    camera.cameraDirection.Normalize();
                        camera.cameraUp = new Vector3(-1, 0, 0);
                    break;
                case "Z":
                    camera.cameraPosition = new Vector3(center.X-30, center.Y+5, center.Z);
                    camera.cameraDirection = center - camera.cameraPosition;
                    camera.cameraDirection.Normalize();
                    camera.cameraUp = new Vector3(0, 1, 0);
                    break;
            }
        }

        public string FindAxis(Primitives pickedCylinder)
        {
            if (Math.Abs(pickedCylinder.rotation.Up.X) == 1f)
            {
                return "X";
            }
            if (Math.Abs(pickedCylinder.rotation.Up.Y) == 1f)
            { 
                return "Y";
            }
            if (Math.Abs(pickedCylinder.rotation.Up.Z) == 1f)
            {
                return "Z";
            }

            return null;
        }
        private void GetLimits()
        {
            min = 0;
            max = leafs.Count;
        }

        public void MouseMoveLeafs(GameTime gt)
        {
            #region MOVE HORIZONTALLY
            if (((gt.TotalGameTime.TotalMilliseconds - moveGameTime) > 100) && (threadIsRunning == false))
            {
               moveGameTime = gt.TotalGameTime.TotalMilliseconds;

                if (Mouse.GetState().RightButton == ButtonState.Pressed)
                {
                    //Fe.rightButtonClick = false;
                    if (end < max)
                    {
                        LoadNextLeafs();
                    }
                }
                if (Mouse.GetState().LeftButton == ButtonState.Pressed)
                {
                    //Fe.leftButtonClick = false;
                    if (start > 0)
                    {
                        LoadPreviousLeafs();
                    }

                }
            }
            #endregion
            #region MOVE VERTICALLY


            if (Fe.mouseWheelState > scrollWheelValue)
            {
                scrollWheelValue = Fe.mouseWheelState;
                startAngle += MathHelper.PiOver4 / 8;
                int i;
                for (i = 0; i < gLeafs.Count; i++)
                {
                    gLeafs[i].fulltransform *= Rotate(-MathHelper.PiOver4 / 8, axis, ((LeafPrimitive)tempGleafs[i]).branchTranslation);
                }
            }
            if (Fe.mouseWheelState < scrollWheelValue)
            {
                scrollWheelValue = Fe.mouseWheelState;
                startAngle -= MathHelper.PiOver4 / 8;
                int i;
                for (i = 0; i < gLeafs.Count; i++)
                {
                    gLeafs[i].fulltransform *= Rotate(MathHelper.PiOver4 / 8, axis, ((LeafPrimitive)tempGleafs[i]).branchTranslation);
                }
            }
            #endregion
        }

        private void KeyboardMoveLeafs(int direction)
        {
            int i = 0;
            if (direction == 0)
            {
                for (i = 0; i < gLeafs.Count; i++)
                {
                    gLeafs[i].fulltransform *= Rotate(MathHelper.PiOver4 / 2, axis, ((LeafPrimitive)tempGleafs[i]).branchTranslation);
                }
            }
            else
            {
                for (i = 0; i < gLeafs.Count; i++)
                {
                    gLeafs[i].fulltransform *= Rotate(-MathHelper.PiOver4 / 2, axis, ((LeafPrimitive)tempGleafs[i]).branchTranslation);
                }
            }
        }

        private Matrix Rotate(float angle, string axis, Matrix branchTranslation)
        {
            Matrix rotation = Matrix.Identity;
            switch (axis)
            {
                case "X":
                    return rotation = Matrix.Invert(branchTranslation) * Matrix.CreateRotationX(angle)*branchTranslation;    

                case "Y":
                    return rotation = Matrix.Invert(branchTranslation) * Matrix.CreateRotationY(angle)*branchTranslation; 

                case "Z":
                    return rotation = Matrix.Invert(branchTranslation) * Matrix.CreateRotationZ(angle) * branchTranslation; 
                    
            }
            return Matrix.Identity;
        }

        private void CopyGleafs()
        {
            if (tempGleafsReady == true)
            {
                gLeafs.Clear();
                int i;
                for (i = start; i < end; i++)
                {
                    gLeafs.Add(tempGleafs[i]);
                }
                tempGleafsReady = false;
                if ((runOnce == false)&&(Fe.par.keyBoardControl))
                {
                    pickedLeaf = gLeafs[0];
                    ((LeafPrimitive)pickedLeaf).picked = true;
                    lastPickedLeaf = pickedLeaf;
                    runOnce = true;
                }
                if (Fe.par.keyBoardControl)
                {
                    kControl.ResetCounter(0);
                    if (previousJump > jumps)  //select last leaf if we go back
                    {
                        SelectPreviousLeaf();
                    }
                }

            }
        }

        public override void Update(GameTime gameTime)
        {
            CopyGleafs();
            if (Fe.par.keyBoardControl == false)
            {
                MouseMoveLeafs(gameTime);
            }
            //update picking every tenth update call (performance optimalization)

            if (gLeafs.Count != 0)
            {
                if (Fe.par.keyBoardControl == false)
                {
                    #region PICKING OPERATIONS
                    if (pickupdated == 0)
                    {

                        pickedLeaf = pick.UpdatePicking(gLeafs, Cursor.cursorRay);

                        if (pickedLeaf != null)
                        {
                            if (lastPickedLeaf != null)
                            {
                                ((LeafPrimitive)lastPickedLeaf).picked = false;

                            }
                            ((LeafPrimitive)pickedLeaf).picked = true;
                            lastPickedLeaf = pickedLeaf;
                        }
                        else
                        {
                            if (lastPickedLeaf != null)
                            {
                                ((LeafPrimitive)lastPickedLeaf).picked = false;
                                ((LeafPrimitive)lastPickedLeaf).opened = false;
                            }
                        }
                        pickupdated++;
                    }
                    else if (pickupdated == 10)
                    {
                        pickupdated = 0;
                    }
                    else
                    {
                        pickupdated++;
                    }


                    #endregion
                }
                else
                {
                    #region KEYBOARD CONTROL
                    
                    int loadNext;

                    if (((gameTime.TotalGameTime.TotalMilliseconds - previousGameTime) > 150)&&(threadIsRunning == false))
                    {
                       previousGameTime = gameTime.TotalGameTime.TotalMilliseconds;
                       pickedLeaf = kControl.LeafUpdate(gLeafs,rootCylinderPatch,out loadNext); //return picked leaf
                       if (loadNext == 2)
                       {
                           if (lastPickedLeaf.id != pickedLeaf.id)
                           {
                               ((LeafPrimitive)lastPickedLeaf).picked = false;
                               #region ROTATE LEAFS
                               if (lastPickedLeaf.id > pickedLeaf.id)
                               {
                                   keyboardRotation--;
                                   if ((((keyboardRotation + 1) % 6) == 0) && (keyboardRotation != -1))
                                   {
                                       KeyboardMoveLeafs(0);
                                   }
                               }
                               else
                               {
                                   keyboardRotation++;
                                   if (((keyboardRotation % 6) == 0) && (keyboardRotation != -1))
                                   {
                                       KeyboardMoveLeafs(1);
                                   }
                               }
                               #endregion
                               lastPickedLeaf = pickedLeaf;
                               ((LeafPrimitive)pickedLeaf).picked = true;
                           }
                           else if (((LeafPrimitive)pickedLeaf).opened == true) //open file
                           {
                               CreateNewThreadForOpenFile(((LeafPrimitive)pickedLeaf).path);
                               ((LeafPrimitive)pickedLeaf).opened = false;
                           }
                       }
                       else if (loadNext == 1)
                       {
                           if (pickedLeaf.id != leafs[max-1].id)
                           {
                               previousJump = jumps;
                               jumps++;
                               keyboardRotation = 0;
                               runOnce = false; //select firts leaf
                               LoadNextLeafs();
                               kControl.ResetCounter(0);
                           }
                       }
                       else if (loadNext == 0)
                       {
                           if (pickedLeaf.id != leafs[0].id)
                           {
                               previousJump = jumps;
                               jumps--;
                               runOnce = false; //select firts leaf
                               keyboardRotation = 0;
                               LoadPreviousLeafs();
                               kControl.ResetCounter(0);
                           }
                       }
 


                    }
                    #endregion
                }
            }
            #region OPEN FILE WITH DEFAULT PROGRAM
            if ((Game.IsActive)&&(Fe.par.keyBoardControl == false))
            {
                if (Fe.doubleClick)
                {
                    Fe.doubleClick = false;
                    if ((pickedLeaf != null) && ((LeafPrimitive)pickedLeaf).opened == false)
                    {
                        CreateNewThreadForOpenFile(((LeafPrimitive)pickedLeaf).path);
                        ((LeafPrimitive)pickedLeaf).opened = true;
                    }
                }
            }
            
            #endregion

            base.Update(gameTime);
        }

        public void CreateNewThreadForOpenFile(string path)
        {
            Thread t = new Thread(delegate() { OpenFile(path); });
            t.Start();
        }

        public static void OpenFile(string path)
        {
            try
            {
                System.Diagnostics.Process.Start(path);
            }
            catch (Exception e)
            {
            }
        }

        private void LoadNextLeafs()
        {
            start += 96;
            end += 96;
            if (end > max)
            {
                end = max;
                //start = max - (max - (max / 96) * 96);
            }
            Thread t = new Thread(delegate() { SetTransform(start, end, 0); });
            threadIsRunning = true;
            t.Start();
        }
        private void LoadPreviousLeafs()
        {
            end = start; 
            start = end - 96;
            if (start <= 0)
            {
                start = 0;
                end = 96;
            }
            Thread t = new Thread(delegate() { SetTransform(start, end, 0); });
            threadIsRunning = true;
            t.Start();
        }

        private void SelectPreviousLeaf()
        {
            KeyboardMoveLeafs(0);
            kControl.ResetCounter(gLeafs.Count - 1);
            keyboardRotation= gLeafs.Count - 1;
            pickedLeaf = gLeafs[gLeafs.Count-1];
            ((LeafPrimitive)lastPickedLeaf).picked = false;
            lastPickedLeaf = pickedLeaf;
            ((LeafPrimitive)pickedLeaf).picked = true;
        }

        public override void Draw(GameTime gameTime)
        {
           Fe.effect.Begin();

            foreach (EffectPass pass in Fe.effect.CurrentTechnique.Passes)
            {
                pass.Begin();
                //Tell the GPU what vertex array to pull vertex data from.
                background.Draw(Fe.effect, Fe.camera, GraphicsDevice);
                GraphicsDevice.DrawUserPrimitives(PrimitiveType.LineStrip, background.bottomBaseLines.ToArray(), 0, 50);
                GraphicsDevice.DrawUserPrimitives(PrimitiveType.LineStrip, background.topBaseLines.ToArray(), 0, 50);
                pass.End();
            }

            Fe.effect.End();
            
            if ((tempGleafsReady == false)&&(gLeafs.Count != 0))
            {
                int i;
                for (i = 0; i < gLeafs.Count; i++)
                {
                    gLeafs[i].Draw(Fe.effect, Fe.camera, GraphicsDevice);
                }
               
            }
            base.Draw(gameTime);
        }



        
    }
}
